module sidearm.token;

import symbol;
import error;

class Token
{
public:

	// properties
	@property size_t length() { return _subTokens.length; }
	@property string typeName() { return toString(_type); }
	@property string name() { return _symbol.name; }

	// operators
	size_t opDollar() { return _subTokens.length; }
	Token opIndex(size_t idx) { return _subTokens[idx]; }
	Token opIndexAssign(Token tok, size_t idx) { return _subTokens[idx] = tok; }
	Token [] opSlice() { return _subTokens[]; }
	Token [] opSlice(size_t a, size_t b) { return _subTokens[a..b]; }
	Token [] opSliceAssign(Token tok) { return _subTokens[] = tok; }
	Token [] opSliceAssign(Token tok, size_t a, size_t b) {
		return _subTokens[a..b] = tok;
	}
	Token [] opSliceAssign(Token[] toks) { return _subTokens[] = toks; }
	Token [] opSliceAssign(Token [] toks, size_t a, size_t b) { return _subTokens[a..b] = toks; }
	int opApply(int delegate(ref Token) dg)
	{
		int result = 0;
		foreach (ref Token tok; _subTokens)
		{
			result = dg(tok);
			if (result) break;
		}
		return result;
	}

	T get(T)(int idx=0) if(is(T : Token) && !is(T == Token) && __traits(compiles,T.staticName))
	{
		enforce(idx >= 0, "index must be positive");
		foreach(tok; _subTokens)
			if (auto staticTok = cast(T)tok)
			{
				if (idx == 0)
					return staticTok;
				else
					index--;
			}
		return null;
	}

	Token get()(string symbolName, int index=0)
	{
		enforce(index >= 0, "index must be positive");
		_confirmValidSymbol(symbolName);

		foreach(tok; _subTokens)
			if(tok.name == symbolName)
			{
				if(index == 0)
					return tok;
				else
					index--;
			}

		return null;
	}

	Token get()(string[] symbolNames)
	{
		if(symbolNames.length == 0)
			return this;

		auto symName = symbolNames[0];
		auto subTok = get(symName);
		if(subTok)
			return subTok.get(symbolNames[1..$]);
		else
			return null;
	}

	T getRequired(T)(int index=0) if(is(T : Token) && !is(T == Token))
	{
		auto result = get!(T)(index);

		if(result is null)
			throw new Exception("Token not found");

		return result;
	}

	Token getRequired()(string symbolName, int index=0)
	{
		auto result = get(symbolName, index);

		if(result is null)
			throw new Exception(`Doesn't exist: getRequired(%s, %s)`.format(symbolName.escapeDDQS(), index));

		return result;
	}

	Token getRequired()(string[] symbolNames)
	{
		auto result = get(symbolNames);

		if(result is null)
			throw new Exception("Token not found");

		return result;
	}

	override string toString()
	{
		return null;
	}



private:

	void		_confirmValidSymbol(string sym)
	{
		if (!_instr.isSymbolNameValid(sym))
			throw new Exception(
					"'%s' is not a valid symbol name"
						.format(sym)
								);
	}


	string		_content = "";
	SymbolType	_type;
	Symbol		_symbol;

	Token []	_subTokens;	// if this is a non-terminal
}
